package classfile

/*
	可以把常量池中的常量分为两类：字面量（literal）和符号引用
	（symbolic reference）。字面量包括数字常量和字符串常量，符号引
	用包括类和接口名、字段和方法信息等。除了字面量，其他常量都
	是通过索引直接或间接指向CONSTANT_Utf8_info常量


	常量池实际上也是一个表
	1）表头给出的数字比实际的常量池大1，假定表头给出的值是n，那么常量池的实际大小是n-1
	2）有效的常量池索引是1 ～ n-1 0是无效的索引，表示不指向任何常量。
	     0这个索引值的应用，比如java.lang.Object没有父类，其super_class索引值就为0
	3）CONSTANT_Long_info和CONSTANT_Double_info各占2个位置，也就是说，如果常量池中
		存在这两种常量，实际的常量数量比n-1还要少，而且1～n-1的某些数也会变成无效索引
*/

//常量池是ConstantInfo类型的数组
type ConstantPool []ConstantInfo

/*
	常量池是在主版本号之后
*/
func readConstantPool(reader *ClassReader) ConstantPool {
	cpCount := int(reader.readUint16())
	cp := make([]ConstantInfo, cpCount)

	//索引从1开始
	for i := 1; i < cpCount; i++ {
		cp[i] = readConstantInfo(reader, cp)
		// http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.5
		// All 8-byte constants take up two entries in the constant_pool table of the class file.
		// If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item in the constant_pool
		// table at index n, then the next usable item in the pool is located at index n+2.
		// The constant_pool index n+1 must be valid but is considered unusable.
		switch cp[i].(type) {
		case *ConstantLongInfo, *ConstantDoubleInfo:
			i++
		}
	}

	return cp
}

/*
	按索引查找常量
*/
func (self ConstantPool) getConstantInfo(index uint16) ConstantInfo {
	if cpInfo := self[index]; cpInfo != nil {
		return cpInfo
	}
	panic("无效的常量池索引")
}

func (self ConstantPool) getNameAndType(index uint16) (string, string) {
	ntInfo := self.getConstantInfo(index).(*ConstantNameAndTypeInfo)
	name := self.getUtf8(ntInfo.nameIndex)
	_type := self.getUtf8(ntInfo.descriptorIndex)
	return name, _type
}

func (self ConstantPool) getClassName(index uint16) string {
	classInfo := self.getConstantInfo(index).(*ConstantClassInfo)
	return self.getUtf8(classInfo.nameIndex)
}

func (self ConstantPool) getUtf8(index uint16) string {
	utf8Info := self.getConstantInfo(index).(*ConstantUtf8Info)
	return utf8Info.str
}
